{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Connecting Networks\n",
    "**scikit-rf** supports the connection of arbitrary ports of N-port networks. It accomplishes this using an algorithm called sub-network growth[[1]](#References), available through the function `connect()`. Note that this function takes into account port impedances. If two connected ports have different port impedances, an appropriate impedance mismatch is inserted. This capability is illustrated here with situations often encountered."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import skrf as rf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cascading 2-port and 1-port Networks\n",
    "A common problem is to connect two Networks one to the other, also known as cascading Networks, which creates a new Network. The figure below illustrates sile simple situations, where the port numbers are identified in gray:\n",
    "\n",
    "<img src=\"figures/networks_connecting_2_2ports.svg\" width=\"600\">\n",
    "\n",
    "or,\n",
    "\n",
    "<img src=\"figures/networks_connecting_2port_1port.svg\" width=\"600\">\n",
    "\n",
    "\n",
    "Let's illustrate this by connecting a transmission line (2-port Network) to a short-circuit (1-port Network) to create a delay short (1-port Network):\n",
    "\n",
    "<img src=\"figures/networks_delay_short.svg\" width=\"600\">\n",
    "\n",
    "Cascading Networks being a frequent operation, it can done conveniently through the `**` operator or with the `cascade` function: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "line = rf.data.wr2p2_line  # 2-port\n",
    "short = rf.data.wr2p2_short  # 1-port\n",
    "\n",
    "delayshort = line ** short  # --> 1-port Network\n",
    "print(delayshort)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or, equivalently using the `cascade()` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "delayshort2 = rf.cascade(line, short)\n",
    "print(delayshort2 == delayshort)  # the result is the same"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is of course possible to connect two 2-port Networks together using the `connect()` function. The `connect()` function requires the Networks and the port numbers to connect together. In our example, the port 1 of the line is connected to the port 0 of the short: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "delayshort3 = rf.connect(line, 1, short, 0)\n",
    "print(delayshort3 == delayshort)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One often needs to cascade a chain Networks together:\n",
    "\n",
    "<img src=\"figures/networks_connecting_N_2ports.svg\" width=\"700\">\n",
    "or, \n",
    "<img src=\"figures/networks_connecting_N_2ports_1port.svg\" width=\"700\">\n",
    "\n",
    "\n",
    "which can be realized using chained `**` or the convenient function `cascade_list`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "line1 = rf.data.wr2p2_line  # 2-port\n",
    "line2 = rf.data.wr2p2_line  # 2-port\n",
    "line3 = rf.data.wr2p2_line  # 2-port\n",
    "line4 = rf.data.wr2p2_line  # 2-port\n",
    "short = rf.data.wr2p2_short  # 1-port\n",
    "\n",
    "chain1 = line1 ** line2 ** line3 ** line4 ** short\n",
    "\n",
    "chain2 = rf.cascade_list([line1, line2, line3, line4, short])\n",
    "\n",
    "print(chain1 == chain2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cascacing 2N-port Networks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The cascading operator `**` also works for to 2N-port Networks, width the following port scheme: \n",
    "\n",
    "<img src=\"figures/networks_connecting_2_2Nports.svg\" width=\"600\">\n",
    "\n",
    "It also works for multiple 2N-port Network. For example, assuming you want to cascade three 4-port Network `ntw1`, `ntw2` and `ntw3`, you can use:\n",
    "```\n",
    "resulting_ntw = ntw1 ** ntw2 ** ntw3\n",
    "``` \n",
    "This is illustrated in [this example on balanced Networks](../examples/networktheory/Balanced%20Network%20De-embedding.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cascading Multi-port Networks\n",
    "To make specific connections between multi-port Networks, two solutions are available, which mostly depends of the complexity of the circuit one wants to build:\n",
    "\n",
    "* For reduced number of connection(s): the `connect()` function\n",
    "\n",
    "* For advanced connections between many arbitrary N-port Networks, the `Circuit` object is more relevant since it allows defining explicitly the connections between ports and Networks. For more information, please refer to the [Circuit documentation](Circuit.ipynb). \n",
    "\n",
    "As an example, terminating one of the port of an a 3-port Network, such as an ideal 3-way splitter:\n",
    "\n",
    "<img src=\"figures/networks_connecting_3port_1port.svg\" width=\"600\">\n",
    "\n",
    "can be done like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tee = rf.data.tee"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To connect port `1` of the tee, to port `0` of the delay short,\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminated_tee = rf.connect(tee, 1, delayshort, 0)\n",
    "terminated_tee"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous example, the port #2 of the 3-port Network `tee` becomes the port #1 of the resulting 2-port Network. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Multiple Connections of Multi-port Networks\n",
    "Keeping track of the port numbering when using multiple time the `connect` function can be tedious (this is the reason why the [Circuit object](Circuit.ipynb) can be simpler to use).\n",
    "\n",
    "Let's illustrate this with the following example: connecting the port #1 of a tee-junction (3-port) to the port #0 of a transmission line (2-port):\n",
    "\n",
    "<img src=\"figures/networks_connecting_3port_2port.svg\" width=\"600\">\n",
    "\n",
    "To keep track of the port scheme after the connection operation, let's change the port characteristic impedances (in red in the figure above):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tee.z0 = [1, 2, 3]\n",
    "line.z0 = [10, 20]\n",
    "# the resulting network is:\n",
    "rf.connect(tee, 1, line, 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## References\n",
    "\n",
    "\n",
    "[1] Compton, R.C.; , \"Perspectives in microwave circuit analysis,\" Circuits and Systems, 1989., Proceedings of the 32nd Midwest Symposium on , vol., no., pp.716-718 vol.2, 14-16 Aug 1989. URL: http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=101955&isnumber=3167"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
